"""推荐应用服务模块

提供了处理推荐相关业务逻辑的函数和服务类。
"""

from typing import Any, List, Optional

from pydantic import BaseModel
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similarity


class Item(BaseModel):
    """项目模型类"""

    id: int  # 项目ID
    attributes: dict  # 项目属性


class UserPreferences(BaseModel):
    """用户偏好模型类"""

    preferences: Optional[dict] = None  # 用户偏好设置
    watch_history: Optional[List[int]] = []  # 观看历史


def combine_attributes(item: Item) -> str:
    """
    将项目的属性组合成一个用于向量化的字符串。
    如果属性是列表（例如类型），则将其展平为空格分隔的字符串。

    Args:
        item: 项目对象

    Returns:
        str: 组合后的属性字符串
    """
    combined_attributes = []

    for value in item.attributes.values():
        if isinstance(value, list):
            # 将列表展平为空格分隔的字符串
            combined_attributes.append(" ".join(v.lower() for v in value))
        else:
            # 将值转换为字符串并转为小写
            combined_attributes.append(str(value).lower())

    return " ".join(combined_attributes).strip()


def get_recommendations(
    user_preferences: UserPreferences, items: list[Item], top_n: int = 10
) -> list[Item]:
    """
    根据用户偏好获取推荐项目

    Args:
        user_preferences: 用户偏好对象
        items: 项目列表
        top_n: 返回的推荐项目数量

    Returns:
        list[Item]: 推荐的项目列表
    """
    # 将项目属性组合成一个用于向量化的文本字段
    combined_tags = [combine_attributes(item) for item in items]

    # 初始化CountVectorizer
    cv = CountVectorizer(max_features=10000, stop_words="english")

    # 将用户偏好组合成一个文本字段
    raw_user_tags = []
    if user_preferences.preferences:
        for key, value in user_preferences.preferences.items():
            if isinstance(value, list):
                raw_user_tags += [str(v).lower() for v in value]
            else:
                raw_user_tags.append(str(value).lower())

    user_tags = " ".join(raw_user_tags)

    # 在项目和用户偏好上拟合CountVectorizer
    cv.fit(combined_tags + [user_tags])

    # 向量化组合标签和用户偏好
    item_vectors = cv.transform(combined_tags).toarray()
    user_vector = cv.transform([user_tags]).toarray()

    # 计算用户偏好与所有项目向量之间的余弦相似度
    similarity_scores = cosine_similarity(user_vector, item_vectors)[0]

    # 按相似度分数对项目进行排序并获取top_n个项目
    ranked_items = sorted(
        enumerate(similarity_scores), key=lambda x: x[1], reverse=True
    )

    # 排除用户已经交互过的项目
    ranked_items = [
        items[i]
        for i, score in ranked_items
        if items[i].id not in user_preferences.watch_history
    ]

    # 返回top_n个项目
    return ranked_items[:top_n]
